import React from 'react';
import EmptyBgSvg from './images/empty-bg.svg';
import './index.less';
import cn from 'classnames';
import PropTypes from 'prop-types';
import { Empty, message, Modal, Radio, Spin, Tabs } from 'antd';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import _ from 'lodash';
import { actions as assetManageActions } from '../../reducer';
import moment from 'moment';
import { IP_RE, IP_V6 } from '@util/constants';
import { getAssetVulRecord, getSiteChangeRecord, loadAssetDetail, updateAsset } from '@api/asset';
import IpAliveCard from '@components/ip-alive-card';
import RecordTimeline from './components/record-time-line';
import SiteChangeRecord from './components/site-change-record';
import VulFindRecord from './components/vul-find-record';
import './flag.css';
import LeftContent from '@containers/asset-manage/components/asset-content/components/left-content';
import RightContent from '@containers/asset-manage/components/asset-content/components/right-content';
import VulInfoTab from '@containers/asset-manage/components/asset-content/components/vul-info-tab';
import PortInfoTab from '@containers/asset-manage/components/asset-content/components/port-info-tab';

const OriginDescInRecord = {
  hand: '通过人工添加创建该资产',
  active: '通过主动探测发现该资产',
  switch: '通过交换机采集发现该资产',
  'zoomeye.org': '通过公网查询添加该资产',
  passive: '通过被动探测添加该资产'
};

class AssetContent extends React.Component {
  static propTypes = {
    className: PropTypes.string,
    currentAssetId: PropTypes.string,
    assetCategoryTree: PropTypes.array,
    setState: PropTypes.func,
    loadAssets: PropTypes.func,
    requestLoadAssetStatsCallback: PropTypes.func,
    loadAssetIndex: PropTypes.number,
    currentAssetUpdated: PropTypes.string,
    currentAssetList: PropTypes.array,
    loadAssetIndustry: PropTypes.func,
    loadAssetCategoryList: PropTypes.func,
    modules: PropTypes.array
  };

  static defaultState = {
    loadingDetail: false,
    currentDetail: {},
    loadedDetail: {},
    currentExtra: 'portInfo',
    currentRecordTaskResult: null,
    ipError: false,
    urlError: false,
    nameError: false,
    emptyError: false,
    searchKeyWordDataType: ''
  };

  constructor(props) {
    super(props);
    this.state = _.cloneDeep(AssetContent.defaultState);
  }

  componentDidUpdate(prevProps) {
    // 检查是否切换资产或刷新资产
    const { currentAssetId, loadAssetIndex, currentAssetUpdated } = this.props;

    const assetChanged =
      currentAssetId !== prevProps.currentAssetId ||
      loadAssetIndex !== prevProps.loadAssetIndex ||
      currentAssetUpdated !== prevProps.currentAssetUpdated;

    if (assetChanged) {
      this.setState(_.cloneDeep(AssetContent.defaultState), this.loadAssetDetail);
    }
  }

  componentWillUnmount() {
    this.props.setState({ currentAssetId: '' });
  }

  checkUpdateAsset = prevDetail => {
    const { currentDetail } = this.state;
    if (_.isEmpty(prevDetail) || _.isEmpty(currentDetail) || !_.get(currentDetail, '_id')) {
      return;
    }
    currentDetail.ipv6 ? (currentDetail.ip = currentDetail.ipv6) : null;
    if (!_.isEqual(this.getUpdateBody(currentDetail), this.getUpdateBody(prevDetail))) {
      this.updateAsset();
    }
  };

  getUpdateBody = detail => {
    const keys = [
      'name',
      'ip',
      'url',
      'weight',
      'tag_oids',
      'customAttributes',
      'assetCategories',
      'industry',
      'staffOid',
      'ipv6'
    ];
    const update = _.pick(detail, keys);
    update.organization = _.last(_.get(detail, 'organizationPaths', [])) || null;
    update.staffOid = _.get(detail, 'staffOid._id', null);

    return _.pickBy(update, i => i !== void 0);
  };

  updateAsset = () => {
    const { currentDetail, nameError, ipError, urlError, emptyError } = this.state;

    if (nameError || ipError || urlError || emptyError) {
      return;
    }

    if (!_.get(currentDetail, '_id')) {
      return message.error('切换资产过快，请重新编辑资产');
    }
    delete currentDetail.ipv6;
    const update = this.getUpdateBody(currentDetail);
    updateAsset({
      siteId: currentDetail._id,
      set: update
    })
      .then(res => {
        const isUpdateAssetCategory = _.get(res, 'data.data.isUpdateAssetCategory');
        const isUpdateIndustry = _.get(res, 'data.data.isUpdateIndustry');
        if (isUpdateAssetCategory) {
          this.props.loadAssetCategoryList();
        }
        if (isUpdateIndustry) {
          this.props.loadAssetIndustry();
        }

        const updated = _.get(res, 'data.data.site');
        this.setCurrentDetail(Object.assign(currentDetail, updated));
        const listData = _.get(res, 'data.data.listData');
        this.syncCurrentAssetInList(listData);
        this.props.requestLoadAssetStatsCallback();
      })
      .catch(err => {
        if (err.type === 'parsed' && err.message) {
          message.error(`更新资产失败,${err.message}`);
        } else {
          message.error('更新资产失败');
        }
      });
  };

  getAssetVulRecord = ({ page, pageSize }) => {
    const siteId = this.props.currentAssetId;

    return getAssetVulRecord({ siteId, page, pageSize })
      .then(res => {
        const list = _.get(res, 'data.data.list', []);
        let count = _.get(res, 'data.data.count', 0);
        const last = _.get(res, 'data.data.last');

        // 将最后一个记录单独提取出来
        if (last && _.get(list, [list.length - 1, '_id']) === last._id) {
          list.pop();
        }
        if (count) {
          count = count - 1;
        }

        const lastElement = last ? <VulFindRecord record={last} /> : null;
        return { list, count, lastElement };
      })
      .catch(() => {
        message.error('获取漏洞发现记录失败');
      });
  };

  getSiteChangeRecord = ({ page, pageSize }) => {
    const siteId = this.props.currentAssetId;
    const { currentDetail } = this.state;

    return getSiteChangeRecord({ siteId, page, pageSize })
      .then(res => {
        const list = _.get(res, 'data.data.list', []);
        const count = _.get(res, 'data.data.count', 0);
        const lastElement = (
          <div className="create-asset">
            <div className="time">{_.get(currentDetail, 'created', '-')} 创建</div>
            <div className="detail">
              {' '}
              {OriginDescInRecord[_.get(currentDetail, 'origin')] || '-'}{' '}
            </div>
          </div>
        );
        return { list, count, lastElement };
      })
      .catch(() => {
        message.error('获取资产变更记录失败');
      });
  };

  setCurrentDetail = currentDetail => {
    currentDetail.organizationPaths = _.get(currentDetail, 'organization.paths', [])
      .concat(_.get(currentDetail, 'organization._id'))
      .filter(Boolean);
    _.forEach(currentDetail.customAttributes, item => delete item._id);
    // 解决深拷贝问题
    this.setState({ currentDetail: _.clone(currentDetail), loadedDetail: currentDetail });
  };

  // 将更新后的数据同步到资产列表
  syncCurrentAssetInList = listData => {
    const siteId = _.get(listData, '_id');
    const { currentAssetList = [], setState } = this.props;
    const index = siteId ? _.findIndex(currentAssetList, i => i._id === siteId) : -1;
    if (index !== -1) {
      currentAssetList[index] = Object.assign(currentAssetList[index], listData);
      setState({ currentAssetList: _.clone(currentAssetList) });
    } else {
      // 资产列表没有相同资产，刷新资产列表
      this.props.loadAssets({ enableLoading: false, syncCurrentAssetUpdated: false });
    }
  };

  loadAssetDetail = () => {
    if (!this.props.currentAssetId) {
      return;
    }
    this.setState({ loadingDetail: true });

    loadAssetDetail(this.props.currentAssetId)
      .then(res => {
        const currentDetail = _.get(res, 'data.data.site');
        this.setCurrentDetail(currentDetail);
      })
      .catch(() => {
        message.error('加载资产详情失败');
      })
      .finally(() => {
        this.setState({ loadingDetail: false });
      });
  };

  onChangeInformation = (type, content) => {
    this.setState({
      informationType: type,
      informationContent: content
    });
  };

  onChangeExtraInfo = e => {
    const key = e.target.value;
    const actionMap = {
      // vulFindRecord: this.getAssetVulRecord
    };
    const action = actionMap[key];

    this.setState({ currentExtra: key }, () => {
      if (action && typeof action === 'function') {
        action();
      }
    });
  };

  onChangeCustomAttributeValue = (attributeOid, value, callback) => {
    const nextDetail = _.cloneDeep(this.state.currentDetail);
    const customAttributes = _.get(nextDetail, 'customAttributes', []);
    const preAttribute = _.find(customAttributes, i => i.attributeOid === attributeOid);
    if (preAttribute) {
      preAttribute.value = value;
    } else {
      customAttributes.push({ attributeOid, value });
    }

    this.setState({ currentDetail: nextDetail }, () => {
      if (typeof callback === 'function') {
        callback();
      }
    });
  };

  onChangeSiteContent = (path, value, callback) => {
    const nextDetail = _.cloneDeep(this.state.currentDetail);
    _.set(nextDetail, path, value);

    // 检查是否有错误
    const name = _.get(nextDetail, 'name');
    const ip = _.get(nextDetail, 'ip');
    const ipv6 = _.get(nextDetail, 'ipv6');
    const url = _.get(nextDetail, 'url');
    const nameError = name && name.length > 32;
    const ipError = ip && !(IP_RE.test(ip) || IP_V6.test(ip));
    // 取消url的校验
    // const urlError = url && !FULL_URL_RE.test(url);
    const emptyError = !ip && !ipv6 && !url && !name;
    this.setState({ currentDetail: nextDetail, nameError, ipError, emptyError }, () => {
      if (typeof callback === 'function') {
        callback();
      }
    });
  };

  onUpdateSite = () => {
    const { loadedDetail } = this.state;
    this.checkUpdateAsset(loadedDetail);
  };

  onSelectIndustry = value => {
    this.setState({
      searchKeyWordDataType: value
    });
  };

  renderContent = () => {
    const {
      currentDetail = {},
      currentExtra,
      ipError,
      urlError,
      nameError,
      emptyError,
      informationContent,
      informationType,
      searchKeyWordDataType
    } = this.state;
    const { assetCategoryTree, modules = [] } = this.props;
    const hasVul = _.get(currentDetail, 'vulOids.length', 0) > 0;
    const hasOneDayModule = modules.includes('1day');

    //#region 风险点描述
    let riskDesc = '',
      riskDescIndex = 1;
    if (_.get(currentDetail, 'vulLevelCountMap.total')) {
      const { fatal, high, middle, low } = _.get(currentDetail, 'vulLevelCountMap');
      riskDesc += `【${riskDescIndex++}】`;
      const descs = [
        { count: fatal, desc: `${fatal}个严重漏洞` },
        { count: high, desc: `${high}个高危漏洞` },
        { count: middle, desc: `${middle}个中危漏洞` },
        { count: low, desc: `${low}个低危漏洞` }
      ];
      riskDesc += `${_.map(_.filter(descs, i => i.count), 'desc').join('，')}；`;
    }
    if (_.get(currentDetail, 'riskPorts.length')) {
      riskDesc += `【${riskDescIndex++}】高危端口：${_.uniq(currentDetail.riskPorts).join(',')}；`;
    }
    if (_.get(currentDetail, 'riskServices.length')) {
      riskDesc += `【${riskDescIndex++}】高危服务：${_.uniq(currentDetail.riskServices).join(
        ','
      )}；`;
    }
    if (_.get(currentDetail, 'riskComponents.length')) {
      riskDesc += `【${riskDescIndex++}】高危组件：${_.uniq(currentDetail.riskComponents).join(
        ','
      )}；`;
    }
    //#endregion

    return (
      <div className="inner-content">
        {hasOneDayModule && riskDesc.length > 0 && (
          <div className="risk-tips">风险点：{riskDesc}</div>
        )}

        <div className="detail-infos">
          <LeftContent
            onChangeSiteContent={this.onChangeSiteContent}
            onUpdateSite={this.onUpdateSite}
            currentDetail={currentDetail}
            ipError={ipError}
            urlError={urlError}
            nameError={nameError}
            emptyError={emptyError}
            assetCategoryTree={assetCategoryTree}
          />
          <RightContent
            onChangeSiteContent={this.onChangeSiteContent}
            currentDetail={currentDetail}
            onUpdateSite={this.onUpdateSite}
            onSelectIndustry={this.onSelectIndustry}
            searchKeyWordDataType={searchKeyWordDataType}
            onChangeCustomAttributeValue={this.onChangeCustomAttributeValue}
          />
        </div>
        <div className="extra-infos">
          <div className="nav-btns">
            <Radio.Group value={currentExtra} onChange={this.onChangeExtraInfo}>
              <Radio.Button value="portInfo">端口信息</Radio.Button>
              <Radio.Button value="portChangeRecord">资产变更记录</Radio.Button>
              {hasOneDayModule && (
                <>
                  <Radio.Button value="vulInfo">
                    漏洞信息
                    {hasVul && <i className="has-vul-icon" />}
                  </Radio.Button>
                  <Radio.Button value="vulFindRecord">漏洞发现记录</Radio.Button>
                </>
              )}
            </Radio.Group>
          </div>
          <Tabs activeKey={currentExtra} renderTabBar={null}>
            <Tabs.TabPane key="portInfo">
              {
                <PortInfoTab
                  currentDetail={currentDetail}
                  onChangeInformation={this.onChangeInformation}
                />
              }
            </Tabs.TabPane>
            <Tabs.TabPane key="portChangeRecord">
              <RecordTimeline
                active={currentExtra === 'portChangeRecord'}
                className="site-change-records"
                loader={this.getSiteChangeRecord}
                recordComponent={SiteChangeRecord}
                recordComponentProps={{
                  setTaskResultDetail: detail => this.setState({ currentRecordTaskResult: detail })
                }}
              />
            </Tabs.TabPane>
            <Tabs.TabPane key="vulInfo">
              {<VulInfoTab currentDetail={currentDetail} />}
            </Tabs.TabPane>
            <Tabs.TabPane key="vulFindRecord">
              <RecordTimeline
                active={currentExtra === 'vulFindRecord'}
                className="site-change-records"
                loader={this.getAssetVulRecord}
                recordComponent={VulFindRecord}
              />
            </Tabs.TabPane>
          </Tabs>
          <Modal
            maskClosable={false}
            className="asset-content-record-task-detail-modal no-header-bottom-lines information-model"
            visible={informationType !== null && typeof informationType !== 'undefined'}
            title={informationType}
            width={650}
            footer={null}
            onCancel={() => this.onChangeInformation(null, null)}
          >
            <div className="information-content">{informationContent}</div>
          </Modal>
        </div>
      </div>
    );
  };

  renderCurrentRecordTaskDetailModal = () => {
    const { currentRecordTaskResult } = this.state;
    const originInfo = _.get(currentRecordTaskResult, ['record', 'originInfo'], {});
    const origin = _.get(originInfo, 'origin');
    const created = moment(_.get(currentRecordTaskResult, 'record.created')).format(
      'YYYY-MM-DD HH:mm:ss'
    );

    let title = null;
    if (origin === 'Web' || origin === 'Zoomeye') {
      const taskId = _.get(originInfo, 'detectionTaskInfo.taskOid');
      const href = taskId ? `/task-manage/${_.toLower(origin)}-detail/${taskId}` : '#';
      title = (
        <span className="time-title">
          {created}&nbsp;&nbsp;
          <a href={href} target="_blank" rel="noopener noreferrer">
            前往查看任务
          </a>
        </span>
      );
    } else if (origin === 'passive') {
      title = (
        <span className="time-title">
          {created}&nbsp;&nbsp;
          <span>被动测绘采集</span>
        </span>
      );
    } else if (origin === 'zoomeye.org') {
      title = (
        <span className="time-title">
          {created}&nbsp;&nbsp;
          <span>zoomeye.org</span>
        </span>
      );
    }

    return (
      <Modal
        maskClosable={false}
        className="asset-content-record-task-detail-modal no-header-bottom-lines"
        visible={currentRecordTaskResult !== null}
        title={title}
        width={900}
        footer={null}
        onCancel={() => this.setState({ currentRecordTaskResult: null })}
      >
        <div style={{ maxHeight: 600 }}>
          {_.get(currentRecordTaskResult, 'list.length')
            ? currentRecordTaskResult.list.map(item => (
                <IpAliveCard key={item._id} detail={item} defaultExpand={false} />
              ))
            : '本次任务未检测到跟该资产相关的信息'}
        </div>
      </Modal>
    );
  };

  render() {
    const { currentDetail, loadingDetail } = this.state;
    const { className } = this.props;
    const isEmpty = _.isEmpty(currentDetail);
    return (
      <div className={cn('asset-content', className)}>
        <div className="title">资产详情</div>
        {loadingDetail ? (
          <div className="inner-content loading">
            <Spin spinning={loadingDetail} />
          </div>
        ) : isEmpty ? (
          <div className="inner-content empty">
            <Empty image={EmptyBgSvg} description="诶，好像什么都没有" />
          </div>
        ) : (
          this.renderContent()
        )}
        {this.renderCurrentRecordTaskDetailModal()}
      </div>
    );
  }
}

const mapStateToProps = state => {
  return {
    tags: _.get(state, 'tag.tags', []),
    assetCategoryTree: _.get(state, 'assetManage.assetCategoryTree', []),
    currentAssetId: _.get(state, 'assetManage.currentAssetId', {}),
    loadAssetIndex: _.get(state, 'assetManage.loadAssetIndex', 0),
    currentAssetList: _.get(state, 'assetManage.currentAssetList', []),
    currentAssetUpdated: _.get(state, 'assetManage.currentAssetUpdated', ''),
    modules: _.get(state, 'auth.cert.modules', [])
  };
};

const mapDispatchToProps = dispatch => {
  return {
    ...bindActionCreators(assetManageActions, dispatch)
  };
};
export default connect(
  mapStateToProps,
  mapDispatchToProps
)(AssetContent);
